// pulled from viem, modified for our use case
import { createClient, http } from 'viem';
import { toWebAuthnAccount } from 'viem/account-abstraction';
import { privateKeyToAccount } from 'viem/accounts';
import { baseSepolia } from 'viem/chains';
import { encodeFunctionData, keccak256 } from 'viem/utils';
import { beforeEach, describe, expect, vi } from 'vitest';

import { createSmartAccount, sign, wrapSignature } from './createSmartAccount.js';

const privateKey = '0x8d0ec8aa1f67f8c11db3c191d3d66408e148759acd617fa22ab5d5d677a234e9';
const signer = privateKeyToAccount(privateKey);
const signerWebauthn = toWebAuthnAccount({
  credential: { id: 'abc', publicKey: '0xdeadbeef' },
});

const client = createClient({
  transport: http(),
  chain: baseSepolia,
});

describe('encodeCalls', () => {
  it('single', async () => {
    const account = await createSmartAccount({
      client,
      owner: signer,
      ownerIndex: 0,
      address: '0xBb0c1d5E7f530e8e648150fc7Cf30912575523E8',
      factoryData:
        '0x3ffba36f0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001949fc7c88032b9fcb5f6efc7a7b8c63668eae9871b765e23123bb473ff57aa831a7c0d9276168ebcc29f2875a0239cffdf2a9cd1c2007c5c77c071db9264df1d000000000000000000000000000000000000000000000000000000000000002549960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2273496a396e6164474850596759334b7156384f7a4a666c726275504b474f716d59576f4d57516869467773222c226f726967696e223a2268747470733a2f2f7369676e2e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000',
    });

    const callData_1 = await account.encodeCalls([
      { to: '0x0000000000000000000000000000000000000000' },
    ]);
    const callData_2 = await account.encodeCalls([
      { to: '0x0000000000000000000000000000000000000000', value: BigInt(20) },
    ]);
    const callData_3 = await account.encodeCalls([
      {
        to: '0x0000000000000000000000000000000000000000',
        value: BigInt(20),
        data: '0xdeadbeef',
      },
    ]);

    expect(callData_1).toMatchInlineSnapshot(
      `"0xb61d27f60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000"`
    );
    expect(callData_2).toMatchInlineSnapshot(
      `"0xb61d27f60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000"`
    );
    expect(callData_3).toMatchInlineSnapshot(
      `"0xb61d27f60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004deadbeef00000000000000000000000000000000000000000000000000000000"`
    );
  });

  it('batch', async () => {
    const account = await createSmartAccount({
      client,
      owner: signer,
      ownerIndex: 0,
      address: '0xBb0c1d5E7f530e8e648150fc7Cf30912575523E8',
      factoryData:
        '0x3ffba36f0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001949fc7c88032b9fcb5f6efc7a7b8c63668eae9871b765e23123bb473ff57aa831a7c0d9276168ebcc29f2875a0239cffdf2a9cd1c2007c5c77c071db9264df1d000000000000000000000000000000000000000000000000000000000000002549960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2273496a396e6164474850596759334b7156384f7a4a666c726275504b474f716d59576f4d57516869467773222c226f726967696e223a2268747470733a2f2f7369676e2e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000',
    });

    const callData = await account.encodeCalls([
      { to: '0x0000000000000000000000000000000000000000' },
      { to: '0x0000000000000000000000000000000000000000', value: BigInt(20) },
      {
        to: '0x0000000000000000000000000000000000000000',
        value: BigInt(20),
        data: '0xdeadbeef',
      },
    ]);

    expect(callData).toMatchInlineSnapshot(
      `"0x34fcd5be00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004deadbeef00000000000000000000000000000000000000000000000000000000"`
    );
  });
});

describe('decodeCalls', () => {
  it('single', async () => {
    const account = await createSmartAccount({
      client,
      owner: signer,
      ownerIndex: 0,
      address: '0xBb0c1d5E7f530e8e648150fc7Cf30912575523E8',
      factoryData:
        '0x3ffba36f0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001949fc7c88032b9fcb5f6efc7a7b8c63668eae9871b765e23123bb473ff57aa831a7c0d9276168ebcc29f2875a0239cffdf2a9cd1c2007c5c77c071db9264df1d000000000000000000000000000000000000000000000000000000000000002549960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2273496a396e6164474850596759334b7156384f7a4a666c726275504b474f716d59576f4d57516869467773222c226f726967696e223a2268747470733a2f2f7369676e2e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000',
    });

    const calls = [
      {
        to: '0x0000000000000000000000000000000000000000',
        value: BigInt(20),
        data: '0xdeadbeef',
      },
    ] as const;

    const data = await account.encodeCalls(calls);
    const decoded = await account.decodeCalls(data);
    expect(decoded).toEqual(calls);
  });

  it('batch', async () => {
    const account = await createSmartAccount({
      client,
      owner: signer,
      ownerIndex: 0,
      address: '0xBb0c1d5E7f530e8e648150fc7Cf30912575523E8',
      factoryData:
        '0x3ffba36f0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001949fc7c88032b9fcb5f6efc7a7b8c63668eae9871b765e23123bb473ff57aa831a7c0d9276168ebcc29f2875a0239cffdf2a9cd1c2007c5c77c071db9264df1d000000000000000000000000000000000000000000000000000000000000002549960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2273496a396e6164474850596759334b7156384f7a4a666c726275504b474f716d59576f4d57516869467773222c226f726967696e223a2268747470733a2f2f7369676e2e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000',
    });

    const calls = [
      {
        data: '0x',
        to: '0x0000000000000000000000000000000000000000',
        value: BigInt(0),
      },
      {
        data: '0x',
        to: '0x0000000000000000000000000000000000000000',
        value: BigInt(20),
      },
      {
        to: '0x0000000000000000000000000000000000000000',
        value: BigInt(20),
        data: '0xdeadbeef',
      },
    ] as const;

    const data = await account.encodeCalls(calls);
    const decoded = await account.decodeCalls(data);
    expect(decoded).toEqual(calls);
  });

  it('invalid data', async () => {
    const account = await createSmartAccount({
      client,
      owner: signer,
      ownerIndex: 0,
      address: '0xBb0c1d5E7f530e8e648150fc7Cf30912575523E8',
      factoryData:
        '0x3ffba36f0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001949fc7c88032b9fcb5f6efc7a7b8c63668eae9871b765e23123bb473ff57aa831a7c0d9276168ebcc29f2875a0239cffdf2a9cd1c2007c5c77c071db9264df1d000000000000000000000000000000000000000000000000000000000000002549960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2273496a396e6164474850596759334b7156384f7a4a666c726275504b474f716d59576f4d57516869467773222c226f726967696e223a2268747470733a2f2f7369676e2e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000',
    });

    const data = encodeFunctionData({
      abi: account.abi,
      functionName: 'entryPoint',
    });

    await expect(() => account.decodeCalls?.(data)).rejects.toThrowErrorMatchingInlineSnapshot(`
      [BaseError: unable to decode calls for "entryPoint"

Version: viem@2.27.2]
    `);
  });
});

describe('getAddress', () => {
  it('default', async () => {
    const account = await createSmartAccount({
      client,
      owner: signer,
      ownerIndex: 0,
      address: '0xBb0c1d5E7f530e8e648150fc7Cf30912575523E8',
      factoryData:
        '0x3ffba36f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000f39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    });

    const address = await account.getAddress();
    expect(address).toMatchInlineSnapshot(`"0xBb0c1d5E7f530e8e648150fc7Cf30912575523E8"`);
  });
});

describe('getStubSignature', () => {
  it('default: private key', async () => {
    const account = await createSmartAccount({
      client,
      owner: signer,
      ownerIndex: 0,
      address: '0xBb0c1d5E7f530e8e648150fc7Cf30912575523E8',
      factoryData:
        '0x3ffba36f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000f39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    });

    const signature = await account.getStubSignature();
    expect(signature).toMatchInlineSnapshot(
      `"0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000041fffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c00000000000000000000000000000000000000000000000000000000000000"`
    );
  });

  it('default: webauthn', async () => {
    const account = await createSmartAccount({
      client,
      owner: signerWebauthn,
      ownerIndex: 0,
      address: '0xBb0c1d5E7f530e8e648150fc7Cf30912575523E8',
      factoryData: '0x',
    });

    const signature = await account.getStubSignature();
    expect(signature).toMatchInlineSnapshot(
      `"0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001949fc7c88032b9fcb5f6efc7a7b8c63668eae9871b765e23123bb473ff57aa831a7c0d9276168ebcc29f2875a0239cffdf2a9cd1c2007c5c77c071db9264df1d000000000000000000000000000000000000000000000000000000000000002549960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2273496a396e6164474850596759334b7156384f7a4a666c726275504b474f716d59576f4d57516869467773222c226f726967696e223a2268747470733a2f2f7369676e2e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000"`
    );
  });

  it('custom: ownerIndex', async () => {
    {
      const account = await createSmartAccount({
        client,
        owner: signer,
        ownerIndex: 0,
        address: '0xBb0c1d5E7f530e8e648150fc7Cf30912575523E8',
        factoryData: '0x',
      });

      const signature = await account.getStubSignature();
      expect(signature).toMatchInlineSnapshot(
        `"0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000041fffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c00000000000000000000000000000000000000000000000000000000000000"`
      );
    }
  });
});

describe('getNonce', () => {
  beforeEach(() => {
    vi.useFakeTimers();
    vi.setSystemTime(new Date(Date.UTC(2023, 1, 1)));
    return () => {
      vi.useRealTimers();
    };
  });

  it('default', async () => {
    const account = await createSmartAccount({
      client,
      owner: signer,
      ownerIndex: 0,
      address: '0xBb0c1d5E7f530e8e648150fc7Cf30912575523E8',
      factoryData: '0x',
    });

    const nonce = await account.getNonce();
    expect(nonce).toMatchInlineSnapshot('30902162761021348478818713600000n');
  });

  it('args: key', async () => {
    const account = await createSmartAccount({
      client,
      owner: signer,
      ownerIndex: 0,
      address: '0xBb0c1d5E7f530e8e648150fc7Cf30912575523E8',
      factoryData: '0x',
    });

    const nonce = await account.getNonce({ key: BigInt(0) });
    expect(nonce).toMatchInlineSnapshot('0n');
  });
});

describe('userOperation.estimateGas', () => {
  it('default: private key', async () => {
    const account = await createSmartAccount({
      client,
      owner: signer,
      ownerIndex: 0,
      address: '0xBb0c1d5E7f530e8e648150fc7Cf30912575523E8',
      factoryData:
        '0x3ffba36f0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001949fc7c88032b9fcb5f6efc7a7b8c63668eae9871b765e23123bb473ff57aa831a7c0d9276168ebcc29f2875a0239cffdf2a9cd1c2007c5c77c071db9264df1d000000000000000000000000000000000000000000000000000000000000002549960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2273496a396e6164474850596759334b7156384f7a4a666c726275504b474f716d59576f4d57516869467773222c226f726967696e223a2268747470733a2f2f7369676e2e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000',
    });

    const request = await account.userOperation?.estimateGas?.({
      callData: '0xdeadbeef',
    });
    expect(request).toMatchInlineSnapshot('undefined');
  });

  it('default: webauthn', async () => {
    const owner = toWebAuthnAccount({
      credential: { id: 'abc', publicKey: '0xdeadbeef' },
    });

    const account = await createSmartAccount({
      client,
      owner,
      ownerIndex: 0,
      address: '0x',
      factoryData: '0x',
    });

    const request = await account.userOperation?.estimateGas?.({
      callData: '0xdeadbeef',
    });
    expect(request).toMatchInlineSnapshot(
      `
      {
        "verificationGasLimit": 800000n,
      }
    `
    );
  });
});

describe('sign', () => {
  it('default', async () => {
    const account = await createSmartAccount({
      client,
      owner: signer,
      ownerIndex: 0,
      address: '0xBb0c1d5E7f530e8e648150fc7Cf30912575523E8',
      factoryData:
        '0x3ffba36f0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001949fc7c88032b9fcb5f6efc7a7b8c63668eae9871b765e23123bb473ff57aa831a7c0d9276168ebcc29f2875a0239cffdf2a9cd1c2007c5c77c071db9264df1d000000000000000000000000000000000000000000000000000000000000002549960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2273496a396e6164474850596759334b7156384f7a4a666c726275504b474f716d59576f4d57516869467773222c226f726967696e223a2268747470733a2f2f7369676e2e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000',
    });

    const signature = await account.sign({
      hash: '0xd9eba16ed0ecae432b71fe008c98cc872bb4cc214d3220a36f365326cf807d68',
    });

    expect(signature).toBeTruthy();
  });

  it('counterfactual', async () => {
    const account = await createSmartAccount({
      client,
      owner: signer,
      ownerIndex: 0,
      address: '0xBb0c1d5E7f530e8e648150fc7Cf30912575523E8',
      factoryData:
        '0x3ffba36f0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001949fc7c88032b9fcb5f6efc7a7b8c63668eae9871b765e23123bb473ff57aa831a7c0d9276168ebcc29f2875a0239cffdf2a9cd1c2007c5c77c071db9264df1d000000000000000000000000000000000000000000000000000000000000002549960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2273496a396e6164474850596759334b7156384f7a4a666c726275504b474f716d59576f4d57516869467773222c226f726967696e223a2268747470733a2f2f7369676e2e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000',
    });

    const signature = await account.sign({
      hash: '0xd9eba16ed0ecae432b71fe008c98cc872bb4cc214d3220a36f365326cf807d68',
    });

    expect(signature).toBeTruthy();
  });
});

describe('signMessage', () => {
  it('default', async () => {
    const account = await createSmartAccount({
      client,
      owner: signer,
      ownerIndex: 0,
      address: '0xBb0c1d5E7f530e8e648150fc7Cf30912575523E8',
      factoryData:
        '0x3ffba36f0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001949fc7c88032b9fcb5f6efc7a7b8c63668eae9871b765e23123bb473ff57aa831a7c0d9276168ebcc29f2875a0239cffdf2a9cd1c2007c5c77c071db9264df1d000000000000000000000000000000000000000000000000000000000000002549960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2273496a396e6164474850596759334b7156384f7a4a666c726275504b474f716d59576f4d57516869467773222c226f726967696e223a2268747470733a2f2f7369676e2e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000',
    });

    const signature = await account.signMessage({
      message: 'hello world',
    });

    expect(signature).toBe(
      '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000411b4612155185a1a24b5588cd001c9e3f8a3dd7afa51db1ef99aed5c2efccc88e466875762b95b6b512f07e5e4b04b92b3da6138fc7a0a76f0697201ffa69ac6f1c00000000000000000000000000000000000000000000000000000000000000'
    );
  });

  it('counterfactual', async () => {
    const account = await createSmartAccount({
      client,
      owner: signer,
      ownerIndex: 0,
      address: '0xBb0c1d5E7f530e8e648150fc7Cf30912575523E8',
      factoryData:
        '0x3ffba36f0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001949fc7c88032b9fcb5f6efc7a7b8c63668eae9871b765e23123bb473ff57aa831a7c0d9276168ebcc29f2875a0239cffdf2a9cd1c2007c5c77c071db9264df1d000000000000000000000000000000000000000000000000000000000000002549960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2273496a396e6164474850596759334b7156384f7a4a666c726275504b474f716d59576f4d57516869467773222c226f726967696e223a2268747470733a2f2f7369676e2e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000',
    });

    const signature = await account.signMessage({
      message: 'hello world',
    });

    expect(signature).toBe(
      '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000411b4612155185a1a24b5588cd001c9e3f8a3dd7afa51db1ef99aed5c2efccc88e466875762b95b6b512f07e5e4b04b92b3da6138fc7a0a76f0697201ffa69ac6f1c00000000000000000000000000000000000000000000000000000000000000'
    );
  });
});

describe('signUserOperation', () => {
  it('default', async () => {
    const account = await createSmartAccount({
      client,
      owner: signer,
      ownerIndex: 0,
      address: '0xBb0c1d5E7f530e8e648150fc7Cf30912575523E8',
      factoryData:
        '0x3ffba36f0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001949fc7c88032b9fcb5f6efc7a7b8c63668eae9871b765e23123bb473ff57aa831a7c0d9276168ebcc29f2875a0239cffdf2a9cd1c2007c5c77c071db9264df1d000000000000000000000000000000000000000000000000000000000000002549960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2273496a396e6164474850596759334b7156384f7a4a666c726275504b474f716d59576f4d57516869467773222c226f726967696e223a2268747470733a2f2f7369676e2e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000',
    });

    const signature = await account.signUserOperation({
      callData: '0xdeadbeef',
      callGasLimit: BigInt(20),
      maxFeePerGas: BigInt(20),
      maxPriorityFeePerGas: BigInt(20),
      nonce: BigInt(0),
      preVerificationGas: BigInt(20),
      signature: '0xdeadbeef',
      verificationGasLimit: BigInt(20),
    });

    expect(signature).toMatchInlineSnapshot(
      `"0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004165db92565810160e8f5bb6c3ab18e1a3581247c11354972452a6956c5ac7318a051bda6a9048927f5a222e2497389c3de1702b40c98693edccfff3214386a7651c00000000000000000000000000000000000000000000000000000000000000"`
    );
  });
});

describe('function: sign', () => {
  it('private key', async () => {
    const signature = await sign({
      owner: signer,
      hash: keccak256('0xdeadbeef'),
    });
    expect(signature).toMatchInlineSnapshot(
      `"0x23edfbb795637a326d2d80623ee145414953be90e2fb1c095a7d2ad68718b68b3ec55573f4341564efcf9415d2f02f3e0742ce883af7fdeda994f036c11406911c"`
    );
  });

  it('webauthn', async () => {
    const credential = {
      id: 'm1-bMPuAqpWhCxHZQZTT6e-lSPntQbh3opIoGe7g4Qs',
      publicKey:
        '0x7da44d4bc972affd138c619a211ef0afe0926b813fec67d15587cf8625b2bf185f5044ae96640a63b32aa1eb6f8f993006bbd26292b81cb07a0672302c69a866',
    } as const;
    const owner = toWebAuthnAccount({
      credential,
      getFn() {
        return Promise.resolve({
          response: {
            authenticatorData: [
              73, 150, 13, 229, 136, 14, 140, 104, 116, 52, 23, 15, 100, 118, 96, 91, 143, 228, 174,
              185, 162, 134, 50, 199, 153, 92, 243, 186, 131, 29, 151, 99, 5, 0, 0, 0, 0,
            ],
            clientDataJSON: [
              123, 34, 116, 121, 112, 101, 34, 58, 34, 119, 101, 98, 97, 117, 116, 104, 110, 46,
              103, 101, 116, 34, 44, 34, 99, 104, 97, 108, 108, 101, 110, 103, 101, 34, 58, 34, 49,
              80, 49, 79, 71, 74, 69, 121, 74, 122, 65, 50, 82, 74, 95, 74, 52, 82, 71, 89, 120,
              122, 107, 87, 71, 48, 119, 66, 70, 113, 109, 105, 51, 77, 51, 54, 72, 69, 107, 103,
              66, 118, 69, 34, 44, 34, 111, 114, 105, 103, 105, 110, 34, 58, 34, 104, 116, 116, 112,
              58, 47, 47, 108, 111, 99, 97, 108, 104, 111, 115, 116, 58, 53, 49, 55, 51, 34, 44, 34,
              99, 114, 111, 115, 115, 79, 114, 105, 103, 105, 110, 34, 58, 102, 97, 108, 115, 101,
              125,
            ],
            signature: [
              48, 69, 2, 33, 0, 198, 106, 113, 129, 35, 170, 51, 12, 13, 0, 67, 158, 211, 55, 188,
              103, 33, 194, 2, 152, 190, 159, 181, 11, 176, 232, 114, 59, 99, 64, 167, 220, 2, 32,
              101, 188, 55, 216, 145, 203, 39, 137, 83, 114, 45, 10, 147, 246, 218, 247, 132, 221,
              228, 225, 57, 110, 143, 87, 172, 198, 76, 141, 30, 169, 166, 2,
            ],
          },
        } as any);
      },
      rpId: '',
    });

    const signature = await sign({
      owner,
      hash: keccak256('0xdeadbeef'),
    });
    expect(signature).toMatchInlineSnapshot(
      `"0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001c66a718123aa330c0d00439ed337bc6721c20298be9fb50bb0e8723b6340a7dc65bc37d891cb278953722d0a93f6daf784dde4e1396e8f57acc64c8d1ea9a602000000000000000000000000000000000000000000000000000000000000002549960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000867b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a223150314f474a45794a7a4132524a5f4a34524759787a6b574730774246716d69334d333648456b67427645222c226f726967696e223a22687474703a2f2f6c6f63616c686f73743a35313733222c2263726f73734f726967696e223a66616c73657d0000000000000000000000000000000000000000000000000000"`
    );
  });

  it('error: incompat account', async () => {
    await expect(() =>
      sign({
        // @ts-expect-error type not supported
        owner: { address: '0x', type: 'json-rpc' },
        hash: keccak256('0xdeadbeef'),
      })
    ).rejects.toMatchInlineSnapshot(`
      [BaseError: \`owner\` does not support raw sign.

Version: viem@2.27.2]
    `);
  });
});

describe('wrapSignature', () => {
  it('default: private key', async () => {
    expect(
      wrapSignature({
        signature:
          '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c',
      })
    ).toMatchInlineSnapshot(
      `"0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000041fffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c00000000000000000000000000000000000000000000000000000000000000"`
    );
  });

  it('default: webauthn', async () => {
    expect(
      wrapSignature({
        signature:
          '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001949fc7c88032b9fcb5f6efc7a7b8c63668eae9871b765e23123bb473ff57aa831a7c0d9276168ebcc29f2875a0239cffdf2a9cd1c2007c5c77c071db9264df1d000000000000000000000000000000000000000000000000000000000000002549960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2273496a396e6164474850596759334b7156384f7a4a666c726275504b474f716d59576f4d57516869467773222c226f726967696e223a2268747470733a2f2f7369676e2e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000',
      })
    ).toMatchInlineSnapshot(
      `"0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001949fc7c88032b9fcb5f6efc7a7b8c63668eae9871b765e23123bb473ff57aa831a7c0d9276168ebcc29f2875a0239cffdf2a9cd1c2007c5c77c071db9264df1d000000000000000000000000000000000000000000000000000000000000002549960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2273496a396e6164474850596759334b7156384f7a4a666c726275504b474f716d59576f4d57516869467773222c226f726967696e223a2268747470733a2f2f7369676e2e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000"`
    );
  });
});
